package com.tanhua.server.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.tanhua.common.utils.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.concurrent.TimeUnit;

/**
 * ResponseBodyAdvice接口是在Controller执行return之后，
 * 在response返回给浏览器或者APP客户端之前，执行的对response的一些处理。
 * 可以实现对response数据的一些统一封装或者加密等操作
 *
 *      其中方法参数：MethodParameter - Spring中的方法参数封装对象
 *          元信息：
 *              关联的方法 - Method
 *              关联的构造器 - Constructor
 *              关联的方法所包含的注解 - MethodAnnotation      ****
 *              构造器或方法参数的索引 - parameterIndex
 *              构造器或方法参数的类型 - parameterType
 *              构造器或方法参数的泛型类型 - genericParameterType
 *              构造器或方法参数的参数名称 - parameterName
 *              所在的类  - containingClass
 */
@ControllerAdvice   //声明当前类是Controller的通知类
public class MyResponseBodyAdvice implements ResponseBodyAdvice {

    @Value("${tanhua.cache.enable}")
    private Boolean enable;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private static final ObjectMapper MAPPER = new ObjectMapper();

    /**
     * 判断是否要执行beforeBodyWrite方法，true为执行，false不执行
     * @param returnType        Spring中的方法参数封装对象
     * @param converterType
     * @return
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        // 判断 1.是否开启全局缓存开关  2.判断方法上是否含有@GetMapping注解  3.判断方法上是否含有@Cache注解
        return enable && returnType.hasMethodAnnotation(GetMapping.class) && returnType.hasMethodAnnotation(Cache.class);
    }

    /**
     * controller的方法返回数据前所执行的方法
     * @param body                      返回值数据主体                 ***
     * @param returnType                Spring中的方法参数封装对象     ***
     * @param selectedContentType       通过内容协商选择的内容类型
     * @param selectedConverterType     选择写入响应的转换器类型
     * @param request                   当前请求对象                   ***
     * @param response                  当前响应对象
     * @return
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        //1.判断响应体是的数据是否为空
        if (body == null) {
            return null;
        }

        try {
            //声明向redis中存储数据值
            String redisValue = null;
            //2.判断响应体是否是字符串(是否是Json字符串)
            if (body instanceof String) {
                //2.1 如果是json字符串，直接强转，作为redis中存储数据
                redisValue = (String) body;
            } else {
                //2.2 通过ObjectMapper转换为json字符串
                redisValue = MAPPER.writeValueAsString(body);
            }

            //3.生成redis中的key
            String redisKey = RedisCacheInterceptor.createRedisKey(((ServletServerHttpRequest) request).getServletRequest());

            //4.获取当前类上@Cache的注解对象 - 目的是为了获取有效时长
            Cache cache = returnType.getMethodAnnotation(Cache.class);

            //5.将结果数据保存到Redis中
            redisTemplate.opsForValue().set(redisKey, redisValue, Long.valueOf(cache.time()), TimeUnit.SECONDS);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return body;
    }
}